package se19team1e.controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import se19team1e.dao.abstrakt.jaas.AdvertiserDAO;
import se19team1e.dao.abstrakt.jaas.CompanyDAO;
import se19team1e.dao.abstrakt.jaas.DepartmentDAO;
import se19team1e.dao.abstrakt.jaas.JobAdvertDAO;
import se19team1e.dao.factory.DAOFactory;
import se19team1e.dao.factory.DAOFactorySelector;
import se19team1e.entity.AccountDTO;
import se19team1e.entity.AdvertiserDTO;
import se19team1e.entity.CompanyDTO;
import se19team1e.entity.DepartmentDTO;
import se19team1e.entity.JobAdvertDTO;
import se19team1e.exception.DataAccessException;
import se19team1e.gui.JobAdvertSearchForm;
import se19team1e.gui.UpdateJobAdvertForm;
import se19team1e.search.SearchEngine;
import se19team1e.search.SearchEngineFactory;
import se19team1e.util.StringUtil;

/**
 * 
 * The create job advert controller class coordinates the operations between the screen, form, and entity objects for login.
 * 
 * @author Prahlada Rao
 *
 */
public class UpdateJobAdvertController extends BaseController {

    /**
     * Constructs the controller.
     * 
     * @param application Reference to the current application instance.
     */
    public UpdateJobAdvertController( Application application ) {
        super( application );
    }

    /**
     * Initializes the controller.
     * 
     * <p>
     * The create job advert form is initialized and set as the current active screen.
     * </p>
     * <p>
     * Initialization involves the retrieval of all companies and departments in the system.
     * </p>
     * 
     * @param application Reference to the current application instance.
     * @throws DataAccessException Error retrieving companies and departments. 
     */
    public void initialize( JobAdvertDTO jobAdvertDTO ) throws DataAccessException {
        DAOFactory daoFactory = DAOFactorySelector.getDAOFactory();

        // get all companies
        CompanyDAO companyDAO = daoFactory.getCompanyDAO();
        List<CompanyDTO> companies = companyDAO.retrieveAll();
        
        // get all departments
        DepartmentDAO departmentDAO = daoFactory.getDepartmentDAO();
        List<DepartmentDTO> departments = departmentDAO.retrieveAll();
        
        // get screen
        UpdateJobAdvertForm screen = this.getApplication().getScreen( UpdateJobAdvertForm.class );


        if (!StringUtil.isEmptyOrNull(jobAdvertDTO.getDepartmentId())){
        	DepartmentDTO departmentDTO= departmentDAO.retrieveById(jobAdvertDTO.getDepartmentId());
        	jobAdvertDTO.setDepartmentName(departmentDTO.getName());
        }
        
        if (!StringUtil.isEmptyOrNull(jobAdvertDTO.getCompanyId())){
        	CompanyDTO companyDTO= companyDAO.retrieveById(jobAdvertDTO.getCompanyId());
        	jobAdvertDTO.setCompanyName(companyDTO.getName());
        }
        // initialize screen
        screen.initialize(jobAdvertDTO, this, companies, departments );
        
        // set as active screen
        this.getApplication().setCurrentScreen( screen );
    }

    /**
     * Validates the job advert and saves to persistence storage.
     * 
     * @param jobAdvert Job advert to create.
     * @throws DataAccessException 
     */
    public void updateJobAdvert( JobAdvertDTO jobAdvert ) throws DataAccessException {
        // validate first
        if ( !this.validate( jobAdvert ) ) {
            // failure, return
            return;
        }
        
        JobAdvertDAO jobAdvertDAO = DAOFactorySelector.getDAOFactory().getJobAdvertDAO();
        jobAdvertDAO.update(jobAdvert);

        submitJobAdvertToSearchEngine( jobAdvert );
        
        // display success message
        this.displayUpdateSuccessMessage();
    }

    /**
     * Shows the create job advert form again.
     * 
     * <p>
     * This time, only the form is set as the active screen, and no furthur initialization is asked to be done.
     * </p>
     * <p>
     * This is to retain the previous entered values, in case the user had wanted to enter multiple similar job adverts at the same go.
     * </p>
     */
    public void showUpdateJobAdvertForm() {
        // get screen
        UpdateJobAdvertForm screen = this.getApplication().getScreen(UpdateJobAdvertForm.class);
        
        // no need to reinitialize, reuse same content.
        
        // set as active screen
        this.getApplication().setCurrentScreen( screen );
    }

    /**
     * Validates the job advert to see if it is valid for creating.
     * 
     * <p>
     * Returns false if validation fails.
     * </p>
     * <p>
     * Displays an error message if it fails, using a message box.
     * </p>
     * @param jobAdvert Job advert to create.
     * @return True if valid.
     */
    private boolean validate( JobAdvertDTO jobAdvert ) {
        return ( validateTitle( jobAdvert ) && 
                validateDescription( jobAdvert ) && 
                validateCompany( jobAdvert ) && 
                validateDepartment( jobAdvert ) && 
                validateExperience( jobAdvert ) && 
                validateLocation( jobAdvert ) && 
                validateSalary( jobAdvert ) && 
                validateSkills( jobAdvert ) );
    }

    /**
     * Update the job advert to belong to the current logged in user's advertiser id.
     * 
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * @param jobAdvert Job Advert to update.
     */
    void updateJobAdvertAdvertiserOwnership( JobAdvertDTO jobAdvert ) {
        DAOFactory daoFactory = DAOFactorySelector.getDAOFactory();
        
        // get current account (advertiser info)
        AccountDTO account = this.getApplication().getCurrentAccount();
        String accountId = account.getId();

        // get current advertiser info
        AdvertiserDAO advertiserDAO = daoFactory.getAdvertiserDAO();
        AdvertiserDTO advertiser=null;
		try {
			advertiser = advertiserDAO.retrieveById( accountId );
		} catch (DataAccessException e) {
			e.printStackTrace();
		}

        // must be an advertiser here! no need to check for null!

        // update advertiser id
		if (advertiser!=null)
			jobAdvert.setAdvertiserId( advertiser.getId() );
    }

    /**
     * Save the job advert to the persistence storage.
     * 
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * @param jobAdvert Job Advert to save.
     * @throws DataAccessException 
     */
    void saveJobAdvert( JobAdvertDTO jobAdvert ) throws DataAccessException {
        DAOFactory daoFactory = DAOFactorySelector.getDAOFactory();
        
        // save job advert
        JobAdvertDAO jobAdvertDAO = daoFactory.getJobAdvertDAO();
        jobAdvertDAO.create( jobAdvert );
    }

    /**
     * Submit the job advert to the search engine.
     * 
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * @param jobAdvert Job Advert to submit to search engine.
     */
    void submitJobAdvertToSearchEngine( JobAdvertDTO jobAdvert ) {
        // submit to search engine
    	SearchEngine searchEngine = getApplication().getSearchEngine();
        searchEngine.updateIndex( jobAdvert );
    }

    /**
     * Validates only the description of the job advert.
     * 
     * <p>
     * Ensure that the description is entered.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateDescription( JobAdvertDTO jobAdvert ) {
        if ( StringUtil.isEmptyOrNull( jobAdvert.getDescription() ) ) {
            // failure, display error message
            this.displayMissingInputErrorMessage( "description" );
            
            return false; 
        }
        return true;
    }

    /**
     * Validates only the company of the job advert.
     * 
     * <p>
     * Ensure that the company is entered.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateCompany( JobAdvertDTO jobAdvert ) {
        if ( StringUtil.isEmptyOrNull( jobAdvert.getCompanyId() ) ) {
            // failure, display error message
            this.displayMissingInputErrorMessage( "company" );
            
            return false; 
        }
        return true;
    }

    /**
     * Validates only the department of the job advert.
     * 
     * <p>
     * Allows department to be blank, but if it is entered, its parent must be the selected company.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateDepartment( JobAdvertDTO jobAdvert ) {
        // allow no department to be selected
        // but if department is selected, make sure that it belongs to the selected company!
        if ( !StringUtil.isEmptyOrNull( jobAdvert.getDepartmentId() ) ) {
            // validate department belongs to company
            DAOFactory daoFactory = DAOFactorySelector.getDAOFactory();
            DepartmentDAO departmentDAO = daoFactory.getDepartmentDAO();
            DepartmentDTO department=null;
			try {
				department = ( DepartmentDTO ) departmentDAO.retrieveById( jobAdvert.getDepartmentId() );
			} catch (DataAccessException e1) {
				e1.printStackTrace();
			}

			if (department!=null){
				 if ( !jobAdvert.getCompanyId().equals( department.getCompanyId() ) ) {
		                // failure, display error message
		                this.displayMessage( "Error", "Selected department does not belong to company!", new ActionListener() {
		                    @Override
		                    public void actionPerformed( ActionEvent e ) {
		                        // do nothing
		                    }
		                });
		                
		                return false; 
		            }
			}
           
        }
        
        return true;
    }

    /**
     * Validates only the experience of the job advert.
     * 
     * <p>
     * Ensure that the experience is entered.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateExperience( JobAdvertDTO jobAdvert ) {
        if ( StringUtil.isEmptyOrNull( jobAdvert.getExperience() ) ) {
            // failure, display error message
            this.displayMissingInputErrorMessage( "experience" );
            
            return false; 
        }
        return true;
    }

    /**
     * Validates only the location of the job advert.
     * 
     * <p>
     * Ensure that the location is entered.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateLocation( JobAdvertDTO jobAdvert ) {
        if ( StringUtil.isEmptyOrNull( jobAdvert.getLocation() ) ) {
            // failure, display error message
            this.displayMissingInputErrorMessage( "location" );
            
            return false; 
        }
        return true;
    }

    /**
     * Validates only the salary of the job advert. 
     * 
     * <p>
     * Ensure that the salary is greater than 0.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateSalary( JobAdvertDTO jobAdvert ) {
        if ( jobAdvert.getSalary() <= 0 ) {
            // failure, display error message
            this.displayMessage( "Error", "Enter a salary greater than 0", new ActionListener() {
                @Override
                public void actionPerformed( ActionEvent e ) {
                    // do nothing
                }
            });
            
            return false; 
        }
        return true;
    }

    /**
     * Validates only the skills of the job advert.
     * 
     * <p>
     * Ensure that the skills is entered.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateSkills( JobAdvertDTO jobAdvert ) {
        if ( StringUtil.isEmptyOrNull( jobAdvert.getSkills() ) ) {
            // failure, display error message
            this.displayMissingInputErrorMessage( "skills" );
            
            return false; 
        }
        return true;
    }

    /**
     * Validates only the title of the job advert.
     * 
     * <p>
     * Ensure that the title is entered.
     * </p>
     * <p>
     * This method is set to package level access for unit testing purpose.
     * </p>
     * 
     * @param jobAdvert Job advert to validate.
     * @return True if valid.
     */
    boolean validateTitle( JobAdvertDTO jobAdvert ) {
        if ( StringUtil.isEmptyOrNull( jobAdvert.getTitle() ) ) {
            // failure, display error message
            this.displayMissingInputErrorMessage( "title" );
            
            return false; 
        }
        return true;
    }

    /**
     * Displays a message box indicating that creation is success
     */
    private void displayUpdateSuccessMessage() {
        this.displayMessage( "Success", "Job Advert Update", new ActionListener() {
            @Override
            public void actionPerformed( ActionEvent event ) {
               // show the search form
                getApplication().selectSearchJobAdvert();
            }
        });
    }

    /**
     * Displays a message box indicating that input is missing.
     */
    protected void displayMissingInputErrorMessage( String field ) {
        this.displayMessage( "Error", "Missing " + field, new ActionListener() {
            @Override
            public void actionPerformed( ActionEvent event ) {
               // do nothing
            }
        });
    }

    /**
     * Shows the search job advert results screen again.
     * 
     * <p>
     * This time, only the screen is set as the active screen, and no furthur initialization is asked to be done.
     * </p>
     * <p>
     * This is to retain the previous search results.
     * </p>
     */
    public void showSearchJobAdvertResultsScreen() {
        // get screen
        JobAdvertSearchForm screen = this.getApplication().getScreen( JobAdvertSearchForm.class );
        
        // no need to reinitialize, reuse same content.
        
        // set as active screen
        this.getApplication().setCurrentScreen( screen );
    }

}
